package com.techshinogongjian.fp.comm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.util.Log;

import com.fingerUsb.FingerResult;
import com.techshinogongjian.fp.util.FpCoder;
import com.techshinogongjian.fp.util.FpConfig;
import com.techshinogongjian.fp.util.FpConst;
import com.techshinogongjian.fp.util.FpImage;
import com.techshinogongjian.fp.util.Logs;

/**
 * 串口通信
 * 
 * @author wangzhi
 *
 */
public class TwoUsbComm extends IComm {

	private static final String TAG = TwoUsbComm.class.getSimpleName();

	private static final byte DATA_START_BYTE = (byte) 0xFF;
	private static final byte DATA_END_BYTE = (byte) 0xFE;
	private static final int MAX_DATA_SIZE = 1024;

	private static final int MAX_USBFS_BUFFER_SIZE = 16384;
	private final int mWriteTimeout = 1000;

	private static final int DIRECTION_IN = 0x80; // 返回数据
	private static final int DIRECTION_OUT = 0x00;// 无返回

	private static final int RET_ERROR = -1;
	private static final int RET_SUCCESS = 0;

	// 命令行发送缓冲区
	private byte[] bSendBuf = null;
	private int nSendDatLen = 0;
	// 接收缓冲区
	private byte[] bRecvBuf = null;
	private int nRecvDatLen = 0;
	byte[] mRecvTempBuf = new byte[1024 * 4];

	private byte[] mFpTemplate;
	byte readcomm;

	InputStream mInputStream;
	OutputStream mOutputStream;

	private UsbDeviceConnection mConn;
	private UsbEndpoint mInEndpoint;
	private UsbEndpoint mOutEndpoint;
	private int nTimeoutDoComm = 5000; // 单个指令通信超时限定

	private int mCmdID;
	private int mCurrentRetCode;
	private int mTemplateSize;

	private int mBaudState;
	private float mSecurityLevel;
	private String mSn;

	private byte[] mImageBuffer;
	private int mWidth;
	private int mHeight;
	private int mVerifyScore;

	private MsgCallback mCallback;

	public TwoUsbComm(UsbDeviceConnection usbDeviceConnection,
			UsbEndpoint inEndpoint, UsbEndpoint outEndpoint) {
		mConn = usbDeviceConnection;
		mInEndpoint = inEndpoint;
		mOutEndpoint = outEndpoint;

		bSendBuf = new byte[1024];
		bRecvBuf = new byte[1024];
	}

	public int getRecvDatLen() {
		return nRecvDatLen;
	}

	public byte[] getbRecvBuf() {
		return bRecvBuf;
	}

	private boolean isWell(int nRet) {
		if (nRet >= 0)
			return true;
		return false;
	}

	public void setCallback(MsgCallback callback) {
		mCallback = callback;
	}

	@Override
	public int read(byte[] bReadOutBuf, int nStartPos, int nWantReadLen) {
		int nRet = FpConst.TCY_FAIL;

		nRet = mConn.bulkTransfer(mInEndpoint, bReadOutBuf, nWantReadLen,
				nTimeoutDoComm);
		return nRet;
	}

	private byte[] preBytes = { (byte) 0x55, 0x53, 0x42, 0x43,//
			0x10, (byte) 0x90, (byte) 0x04, (byte) 0xa0,//
			0x00, 0x10, 00, 00,//
			00, 00, 0x10, (byte) 0xef, //
			00, 00, 00, 00,//
			00, 00, 00, 00,//
			00, 00, 00, 00, 00, 00, 00 };

	@Override
	public int write(byte[] data) {
		int ret = mConn.bulkTransfer(mOutEndpoint, data, data.length,
				mWriteTimeout);
		return ret;
	}

	public int write(byte[] pbWriteBuf, int nStartPos, int nWantWriteLen) {
		int ret = mConn.bulkTransfer(mOutEndpoint, pbWriteBuf, nWantWriteLen,
				mWriteTimeout);
		return ret;
	}

	private int flush() {
		if (isOpened() == false)
			return FpConst.TCY_FAIL;
		try {
			mOutputStream.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return 0;
	}

	public boolean isOpened() {
		if (mInputStream != null && mOutputStream != null)
			return true;
		return false;
	}

	private int SendByte(int b) {
		if (b == DATA_START_BYTE)
			nSendDatLen = 0;
		bSendBuf[nSendDatLen++] = (byte) b;
		if (b == DATA_END_BYTE) {
			// 发送usb前缀指令
			preBytes[12] = DIRECTION_OUT;
			byte[] length = FpCoder.intTo4Byte(nSendDatLen);
			System.arraycopy(length, 0, preBytes, 8, 4);
			int ret = write(preBytes, 0, preBytes.length);
			HexOut(TAG, preBytes, 0, preBytes.length);

			// 发送指令
			ret = write(bSendBuf, 0, nSendDatLen);
			HexOut(TAG, bSendBuf, 0, nSendDatLen);

			// 接收usb后缀
			ret = read(bRecvBuf, 0, 13);
			HexOut(TAG, bRecvBuf, 0, 13);

			if (ret < 0) {
				Logs.i(TAG, "错误..." + ret);
				return FpConst.TCY_NLNK;
			}
			Logs.i(TAG, "发送成功 ret:" + ret);
			return RET_SUCCESS;
		}
		return RET_ERROR;
	}

	/**
	 * 发送指令
	 * 
	 * @param cmd
	 * @param buffer
	 * @param size
	 * @param errCode
	 */
	private void sendCmd(byte cmd, byte[] buffer, int size, int errCode) {
		mCmdID = (mCmdID + 1) & 0x7F;
		if (mCmdID == 0)
			mCmdID = 1;

		short i;
		byte v, crc;

		crc = 0;
		SendByte(DATA_START_BYTE);

		v = (byte) mCmdID;// command serial number
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);

		v = 0;
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);
		v = 0;
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);

		v = 0;
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);
		v = 0;
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);

		v = (byte) cmd; // command code
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);

		v = (byte) errCode; // return code
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);

		v = (byte) (size >> 8);// high byte of data size
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);
		v = (byte) (size & 0xFF);// low byte of data size
		crc = FpConst.crc8b(v, crc);
		if (v >= 0xfd) {
			SendByte(0xfd);
			SendByte(v - 0xfd);
		} else
			SendByte(v);
		for (i = 0; i < size; i++) {// data
			v = (byte) buffer[i];
			crc = FpConst.crc8b(v, crc);
			if ((v & 0xff) >= 0xfd) {
				SendByte(0xfd);
				SendByte((byte) ((v & 0xff) - (0xfd & 0xff)));
			} else
				SendByte(v);
		}
		if ((crc & 0xff) >= (0xfd & 0xff)) {// crc value
			SendByte(0xfd);
			SendByte(((crc & 0xff) - 0xfd));
		} else
			SendByte(crc);
		SendByte(DATA_END_BYTE);
	}

	private int revData() {
		int nRet = FpConst.TCY_FAIL;
		// 发送usb前缀指令
		long start = System.currentTimeMillis();
		preBytes[8] = 0x00;
		preBytes[9] = 0x10;
		preBytes[10] = 0x00;
		preBytes[11] = 0x00;
		preBytes[12] = (byte) DIRECTION_IN;
		nRet = write(preBytes);
		Logs.i(TAG, "revCmd ret:" + nRet);
		HexOut(TAG, preBytes, 0, preBytes.length);

		// 接收数据
		nRet = read(mRecvTempBuf, 0, mRecvTempBuf.length);
		Log.d(TAG, "revData length:" + nRet);
		HexOut(TAG, mRecvTempBuf, 0, 32);

		// 接收usb后缀数据
		read(bSendBuf, 0, 13);
		HexOut(TAG, bSendBuf, 0, 13);
		Logs.i(TAG, "接收数据花费时间：" + (System.currentTimeMillis() - start) + "ms");

		if (nRet < 0) {
			return FpConst.TCY_NLNK;
		}

		// 校验指令
		long start1 = System.currentTimeMillis();
		for (int i = 0; i < nRet; i++) {
			saveByte(mRecvTempBuf[i]);
		}
		long end = System.currentTimeMillis();
		Logs.i(TAG, "校验数据花费时间：" + (end - start1) + "ms");

		return nRet;
	}

	private int revCmd() {
		int nRet = FpConst.TCY_FAIL;
		setReadReady();

		for (int i = 0; i < 3; i++) {
			setReadReady();
			nRet = revData();

			if ((readcomm & 0x80) == 0) {
				Logs.e(TAG, "1");
				nRet = FpConst.TCY_ERRO;
			}
			if (bRecvBuf[0] != mCmdID) {
				Logs.e(TAG, "2:" + bRecvBuf[0]);
				nRet = FpConst.TCY_ERRO;
			}
			if (bRecvBuf[5] != mCurrentRetCode) {
				Logs.e(TAG, "3：" + bRecvBuf[5] + " code:" + mCurrentRetCode);
				nRet = FpConst.TCY_ERRO;
			}

			if (nRet > 0) {
				break;
			}
		}
		// nRet = revData();
		//
		// if ((readcomm & 0x80) == 0) {
		// Logs.e(TAG, "1");
		// nRet = FpConst.TCY_ERRO;
		// }
		// if (bRecvBuf[0] != mCmdID) {
		// Logs.e(TAG, "2:" + bRecvBuf[0]);
		// nRet = FpConst.TCY_ERRO;
		// }
		// if (bRecvBuf[5] != mCurrentRetCode) {
		// Logs.e(TAG, "3：" + bRecvBuf[5] + " code:" + mCurrentRetCode);
		// nRet = FpConst.TCY_ERRO;
		// }
		//
		// if (nRet < 0) {
		// setReadReady();
		// nRet = revData();
		// }

		if (nRet < 0) {
			return nRet;
		}

		Logs.i(TAG, "errorCode:" + (bRecvBuf[6] & 0xff));
		switch (bRecvBuf[6]) {
		case FpConst.MSG_SUCCESS:
			nRet = FpConst.TCY_SUCC;
			HexOut(TAG, bRecvBuf, 0, nRecvDatLen);
			break;
		case FpConst.MSG_NOT_SUPPORTED_PARAM:
			nRet = FpConst.TCY_PARA;
			setReadReady();
			break;
		case FpConst.MSG_INVALID_VALUE:
			nRet = FpConst.TCY_PARA;
			setReadReady();
			break;
		case FpConst.MSG_FAILURE:
			nRet = FpConst.TCY_FAIL;
			setReadReady();
			break;
		case FpConst.MSG_NOTEXIST:
			nRet = FpConst.TCY_NOT_EXISTS;
			setReadReady();
			break;
		default:
			nRet = FpConst.TCY_FAIL;
			setReadReady();
			break;
		}

		return nRet;
	}

	void setReadReady() {
		bRecvBuf[0] = 0x00;
		readcomm = 0;
		nRecvDatLen = 0;
	}

	/**
	 * 处理串口读入的字节
	 * 
	 * @param b
	 * @return
	 */
	short saveByte(byte b) {
		int i, k;
		if ((readcomm & 0x80) > 0)
			return -1; // 如果串口数据包正在处理，不接受新的数据！
		if (b == DATA_START_BYTE) {
			readcomm |= 0x1;
			nRecvDatLen = 0;
			// Logs.i(TAG, "接收到开头");
			return -1;
		} else {
			if ((readcomm & 0x1) == 0) { // 如果0xFF没收到，不接受数据！
				return -1;
			}
			if (b != DATA_END_BYTE) {// 如果收到的不是结束字节，把数据存入。
				if (nRecvDatLen < MAX_DATA_SIZE * 2)
					bRecvBuf[nRecvDatLen++] = b;
				else {// 数据太长，报错。
					setReadReady();
					return -1;
				}
			} else {// 收到结束字节，包收完。处理包数据。
				readcomm = (byte) 0x80;// 标记包数据正在处理。
				Logs.i(TAG, "标记数据包结束..." + Integer.toHexString((b & 0xff)));
				if (nRecvDatLen < 9) { // 数据包太小，报错！
					setReadReady();
					return -1;
				}
				k = 0;
				for (i = 0; i < nRecvDatLen; i++) {// 将包数据恢复。
					if ((bRecvBuf[i] & 0xff) != 0xfd)
						bRecvBuf[k++] = (byte) (bRecvBuf[i] & 0xff);
					else {
						bRecvBuf[k++] = (byte) ((bRecvBuf[i] & 0xff) + (bRecvBuf[i + 1] & 0xff));
						i++;
					}
				}
				nRecvDatLen = k;
				if (nRecvDatLen < 9) { // 数据包太小，报错！
					setReadReady();
					Logs.i(TAG, "数据包太小，报错");
					return -1;
				}

				byte crc = FpConst.crc8(bRecvBuf, nRecvDatLen - 1);
				if (bRecvBuf[nRecvDatLen - 1] != crc) {// 校验出错！
					Logs.e(TAG,
							"校验出错 实际值："
									+ Integer
											.toHexString(bRecvBuf[nRecvDatLen - 1] & 0xff)
									+ " 校验值：" + Integer.toHexString(crc & 0xff)
									+ " 数据长度：" + nRecvDatLen);
					HexOut(TAG, bRecvBuf, 0, nRecvDatLen);
					setReadReady();
					return FpConst.TCY_ERRO;
				}
				if (bRecvBuf[7] * 256 + bRecvBuf[8] != nRecvDatLen - 10) {// 数据长度出错！
					setReadReady();
					Logs.e(TAG, "数据长度出错");
					return -1;
				}
				// 数据包检验完成！
				Logs.i(TAG, "数据包检验完成！");
			}
		}
		return bRecvBuf[0];
	}

	@Override
	public boolean isCancel() {
		return nCancelFlag == 1;
	}

	public void setCancel() {
		nCancelFlag = 1;
	}

	private void HexOut(String tag, byte[] bOut, int nOffSet, int nLen) {
		Logs.HexOut(tag, bOut, nOffSet, nLen);
	}

	private void sendMsg(int code, byte[] img) {
		if (mCallback != null) {
			mCallback.call(code, img);
		}
	}

	/**
	 * 设置模块为空转状态
	 * 
	 * @return
	 */
	private int cmdIdle() {
		int nRet = FpConst.TCY_SUCC;

		sendCmd((byte) FpConst.CMD_NONE, null, 0, 0);
		return nRet;
	}
	
	/**
	 * 获取设备sn号
	 * @return
	 */
	private int getDeviceSn() {
		int nRet = FpConst.TCY_FAIL;
		mCurrentRetCode = FpConst.RET_GETUID;
		isCommTimeout(nTimeoutOfComm);
		
		byte[] data = new byte[2];
		data[0] = (byte) 0x8f;
		data[1] = (byte) 0xab;

		sendCmd((byte) FpConst.CMD_GETUID, data, 2, 0);

		nRet = revCmd();

		if (nRet == FpConst.TCY_SUCC) {
			byte[] sn = new byte[20];
			System.arraycopy(bRecvBuf, 9, sn, 0, 20);
			HexOut(TAG, bRecvBuf, 0, nRecvDatLen);
			mSn = String.valueOf(sn);
			Logs.i(TAG,"SN:" + mSn);
		}

		return nRet;
	}

	/**
	 * 设置串口波特率
	 * 
	 * @return
	 */
	private int setDeviceBaud() {
		int nRet = FpConst.TCY_FAIL;
		int baud = FpConfig.getBaud();
		mCurrentRetCode = FpConst.RET_SETBAUD;
		byte[] data = new byte[4];

		isCommTimeout(nTimeoutOfComm);
		data[3] = (byte) baud;

		sendCmd((byte) FpConst.CMD_SETBAUD, data, 4, 0);

		nRet = revCmd();

		return nRet;
	}

	/**
	 * 获取串口波特率
	 * 
	 * @return
	 */
	private int getDeviceBaud() {
		int nRet = FpConst.TCY_FAIL;
		mCurrentRetCode = FpConst.RET_GETBAUD;
		isCommTimeout(nTimeoutOfComm);

		sendCmd((byte) FpConst.CMD_GETBAUD, null, 0, 0);

		nRet = revCmd();

		if (nRet == FpConst.TCY_SUCC) {
			byte[] data = new byte[4];
			System.arraycopy(bRecvBuf, 9, data, 0, 4);
			int state = FpCoder.fourByteToInt(data);
			mBaudState = FpConfig.getBaud(state);
		}

		return nRet;
	}

	/**
	 * 设置安全级别
	 * 
	 * @return
	 */
	private int setDeviceLevel() {
		int nRet = FpConst.TCY_FAIL;
		int level = FpConfig.getLevel();
		mCurrentRetCode = FpConst.RET_SETSLEVEL;
		byte[] data = new byte[4];

		isCommTimeout(nTimeoutOfComm);
		data[3] = (byte) level;

		sendCmd((byte) FpConst.CMD_SETSLEVEL, data, 4, 0);

		nRet = revCmd();

		return nRet;
	}

	/**
	 * 获取安全级别
	 * 
	 * @return
	 */
	private int getDeviceLevel() {
		int nRet = FpConst.TCY_FAIL;
		mCurrentRetCode = FpConst.RET_GETSLEVEL;

		isCommTimeout(nTimeoutOfComm);

		sendCmd((byte) FpConst.CMD_GETSLEVEL, null, 0, 0);

		nRet = revCmd();

		if (nRet == FpConst.TCY_SUCC) {
			byte[] data = new byte[4];
			System.arraycopy(bRecvBuf, 9, data, 0, 4);
			int state = FpCoder.fourByteToInt(data);
			mSecurityLevel = FpConfig.getSecurityLevel(state);
		}

		return nRet;
	}

	/**
	 * 设置指纹注册所需的图像个数
	 * 
	 * @return
	 */
	private int setDeviceEnrollImageCount(int newCount) {
		int nRet = FpConst.TCY_FAIL;
		byte[] data = new byte[4];
		mCurrentRetCode = FpConst.RET_SETENROLLIMAGECOUNT;

		isCommTimeout(nTimeoutOfComm);
		data[3] = (byte) newCount;

		sendCmd((byte) FpConst.CMD_SETENROLLIMAGECOUNT, data, 4, 0);

		nRet = revCmd();

		return nRet;
	}

	/**
	 * 指纹录入
	 * 
	 * @return
	 */
	private int setDeviceEntroll(boolean isImageDownload) {
		int nRet = FpConst.TCY_FAIL;
		byte[] data = new byte[6];
		long fid = 0;
		int row = 0, width = 0, height = 0;
		byte[] imageBuffer = null;
		boolean isImageOk = false;
		boolean isEnrollDone = false;

		clear();
		mCurrentRetCode = FpConst.RET_SETENROLL;

		data[0] = (byte) ((fid >> 24) & 0xFF);
		data[1] = (byte) ((fid >> 16) & 0xFF);
		data[2] = (byte) ((fid >> 8) & 0xFF);
		data[3] = (byte) (fid & 0xFF);
		if (isImageDownload) {
			data[4] = (byte) 0xaa;// Ask for image uploaded to host
			data[5] = (byte) 0x88;
			if (FpConfig.isBigPic()) {
				data[5] = (byte) 0x78;
			}
		} else {
			data[4] = data[5] = 0;
		}

		sendCmd((byte) FpConst.CMD_SETENROLL, data, 6, 0);

		preBytes[12] = (byte) DIRECTION_IN;
		byte[] length = FpCoder.intTo4Byte(1024 * 4);
		System.arraycopy(length, 0, preBytes, 8, 4);
		setReadReady();
		while (true) {
			// 取消
			if (nCancelFlag == 1) {
				nCancelFlag = 0;
				nRet = FpConst.TCY_CNCL;
				cmdIdle();
				break;
			}
			// 交易超时
			if (isFuncTimeout() == true) {
				nRet = FpConst.TCY_TMOT;
				Logs.i(TAG, "交易超时...");
				cmdIdle();
				break;
			}

			// 发送usb前缀指令
			nRet = write(preBytes);
			Logs.i(TAG, "revCmd ret:" + nRet);
			HexOut(TAG, preBytes, 0, preBytes.length);

			// 接收数据
			nRet = read(mRecvTempBuf, 0, mRecvTempBuf.length);
			Logs.i(TAG, "接收数据长度：" + nRet);
			HexOut(TAG, mRecvTempBuf, 0, 32);

			// 接收usb后缀数据
			read(bSendBuf, 0, 13);

			if (nRet < 0) {
				return FpConst.TCY_NLNK;
			} else if (nRet == 4096) {
				complete();
			}

			// 校验指令
			for (int i = 0; i < nRet; i++) {
				saveByte(mRecvTempBuf[i]);

				if ((readcomm & 0x80) == 0) {
					continue;
				}
				if (bRecvBuf[0] != mCmdID) {
					setReadReady();
					continue;
				}

				Logs.i(TAG, "进度：" + bRecvBuf[5]);

				/* 处理图像传输数据包 */
				if (bRecvBuf[5] == FpConst.RET_IMAGEPARAM) {// 处理图像参数数据包
					width = (bRecvBuf[9] & 0xff) * 256 + (bRecvBuf[10] & 0xff);
					height = (bRecvBuf[11] & 0xff) * 256
							+ (bRecvBuf[12] & 0xff);
					Log.i(TAG, "width:" + width + " height:" + height);
					mWidth = width - width % 4;
					mHeight = height - height % 4;
					imageBuffer = new byte[mWidth * mHeight];
					isImageOk = true;
					setReadReady();
					continue;
				} else if (bRecvBuf[5] == FpConst.RET_IMAGELINE) {
					if (isImageOk) {
						row = (bRecvBuf[9] & 0xff) * 256
								+ (bRecvBuf[10] & 0xff);
						if (row < height) {
							System.arraycopy(bRecvBuf, 13, imageBuffer, row
									* mWidth, mWidth);
						}
						if (row == height - 1) {
							// 收到最后一行，图像上传 processImage
							mImageBuffer = imageBuffer;
							if (mImageBuffer != null) {
								FpImage fpImage = new FpImage();
								fpImage.saveRawAsBmpBuf(mImageBuffer, mWidth,
										mHeight);
								mImageBuffer = null;
								sendMsg(FpConst.TCY_GETIMAGE,
										fpImage.getBmpImgBuf());
							}
						}
						setReadReady();
						continue;
					}
				}
				if (bRecvBuf[5] != FpConst.RET_SETENROLL) {
					setReadReady();
					continue;
				}

				Logs.e(TAG, "Error:" + bRecvBuf[6]);

				switch (bRecvBuf[6]) {
				case FpConst.MSG_CAPTUREIMAGE:
					sendMsg(FpConst.TCY_CAPTUREIMAGE, null);
					break;
				case FpConst.MSG_ENROLL_START:
					Logs.i(TAG, "录入开始...");
					sendMsg(FpConst.TCY_ENROLL_START, null);
					break;
				case FpConst.TCY_SUCC:
					fid = bRecvBuf[9];
					fid = (fid << 8) + bRecvBuf[10];
					fid = (fid << 8) + bRecvBuf[11];
					fid = (fid << 8) + bRecvBuf[12];
					nRet = FpConst.TCY_SUCC;
					isImageOk = false;
					isEnrollDone = true;
					break;
				case FpConst.MSG_FAILURE:
					sendMsg(FpConst.TCY_FAIL, null);
					isImageOk = false;
					isEnrollDone = true;
					nRet = FpConst.TCY_FAIL;
					break;
				case FpConst.MSG_MEMORY_ERROR:
					sendMsg(FpConst.TCY_FLSH, null);
					isImageOk = false;
					isEnrollDone = true;
					nRet = FpConst.TCY_FLSH;
					break;
				case FpConst.MSG_ERROR:
					sendMsg(FpConst.TCY_FAIL, null);
					isImageOk = false;
					isEnrollDone = true;
					nRet = FpConst.TCY_FAIL;
					break;
				case FpConst.MSG_BADIMAGE:
					sendMsg(FpConst.TCY_NOFP, null);
					nRet = FpConst.TCY_NOFP;
					isImageOk = false;// 本次图像处理完毕，准备接收下幅图像
					break;
				case FpConst.MSG_MOREIMAGE:
					sendMsg(FpConst.TCY_MOREIMAGE, null);
					nRet = FpConst.TCY_MOREIMAGE;
					isImageOk = false;// 本次图像处理完毕，准备接收下幅图像
					break;
				default:
					sendMsg(FpConst.TCY_FAIL, null);
					nRet = FpConst.TCY_FAIL;
					isImageOk = false;
					isEnrollDone = true;
					break;
				}
				setReadReady();
				break;
			}

			if (isEnrollDone) {
				break;
			}
		}

		if (nRet >= 0) {// 注册成功。获取指纹数据
			mTemplateSize = 0;
			nRet = getTemplate2ndID(fid, row);
			if (nRet == FpConst.TCY_SUCC) {
				if (mTemplateSize == 512) {
					if (mFpTemplate == null) {
						mFpTemplate = new byte[mTemplateSize];
					}
					System.arraycopy(bRecvBuf, 17, mFpTemplate, 0,
							mTemplateSize);
				} else {
					nRet = FpConst.TCY_FAIL;
				}
			} else {
				nRet = FpConst.TCY_FAIL;
			}
		} else {
			nRet = nRet == -1 ? FpConst.TCY_FAIL : nRet;
		}

		return nRet;
	}

	private int getTemplate2ndID(long id, int size) {
		int nRet = FpConst.TCY_FAIL;
		byte[] buffer = new byte[4];
		mCurrentRetCode = FpConst.RET_GETTEMPLATE;

		buffer[0] = (byte) ((id >> 24) & 0xFF);
		buffer[1] = (byte) ((id >> 16) & 0xFF);
		buffer[2] = (byte) ((id >> 8) & 0xFF);
		buffer[3] = (byte) (id & 0xFF);

		sendCmd((byte) FpConst.CMD_GETTEMPLATE, buffer, 4, 1);

		preBytes[12] = (byte) DIRECTION_IN;
		byte[] length = FpCoder.intTo4Byte(4096);
		System.arraycopy(length, 0, preBytes, 8, 4);

		while (true) {
			// 取消
			if (nCancelFlag == 1) {
				nCancelFlag = 0;
				nRet = FpConst.TCY_CNCL;
				cmdIdle();
				break;
			}
			// 交易超时
			if (isFuncTimeout() == true) {
				nRet = FpConst.TCY_TMOT;
				Logs.i(TAG, "交易超时...");
				cmdIdle();
				break;
			}

			// 发送usb前缀指令
			nRet = write(preBytes);
			Logs.i(TAG, "getTemplate2ndID ret:" + nRet);
			HexOut(TAG, preBytes, 0, preBytes.length);

			// 接收数据
			nRet = read(mRecvTempBuf, 0, mRecvTempBuf.length);
			Logs.i(TAG, "接收特征长度：" + nRet);
			HexOut(TAG, mRecvTempBuf, 0, 32);

			// 接收usb后缀
			read(bSendBuf, 0, 13);

			if (nRet == 0) {
				continue;
			}
			if (nRet < 0) {
				cmdIdle();
				return FpConst.TCY_NLNK;
			}

			// 校验指令
			for (int i = 0; i < nRet; i++) {
				saveByte(mRecvTempBuf[i]);
			}

			if (bRecvBuf[5] != mCurrentRetCode) {
				return FpConst.TCY_FAIL;
			}
			if ((readcomm & 0x80) == 0) {
				continue;
			}

			switch (bRecvBuf[6]) {
			case FpConst.MSG_INVALID_VALUE:
				nRet = FpConst.TCY_PARA;
				break;
			case FpConst.MSG_SUCCESS:
				Logs.i(TAG, "获取特征成功...");
				size = bRecvBuf[13] * 256 + bRecvBuf[14] - 2;
				mTemplateSize = size;
				nRet = FpConst.TCY_SUCC;
				break;
			case FpConst.MSG_FAILURE:
				nRet = FpConst.TCY_FAIL;
				break;
			case FpConst.MSG_ERROR:
				nRet = FpConst.TCY_FAIL;
				break;
			default:
				nRet = FpConst.TCY_FAIL;
				break;
			}
			break;
		}

		return nRet;
	}

	/**
	 * 验证指纹
	 * 
	 * @return
	 */
	private int setDeviceVerify(long fid, boolean isImageDownload) {
		int nRet = FpConst.TCY_FAIL;
		byte[] data = new byte[6];
		int row = 0, width = 0, height = 0;
		byte[] imageBuffer = null;
		boolean isImageOk = false;
		boolean isVerifyDone = false;
		mCurrentRetCode = FpConst.RET_SETVERIFY;

		data[0] = (byte) ((fid >> 24) & 0xFF);
		data[1] = (byte) ((fid >> 16) & 0xFF);
		data[2] = (byte) ((fid >> 8) & 0xFF);
		data[3] = (byte) (fid & 0xFF);

		if (isImageDownload) {
			data[4] = (byte) 0xaa;// Ask for image uploaded to host
			data[5] = (byte) 0x88;
			if (FpConfig.isBigPic()) {
				data[5] = (byte) 0x78;
			}
		} else {
			data[4] = data[5] = 0;
		}

		sendCmd((byte) FpConst.CMD_SETVERIFY, data, 6, 0);

		preBytes[12] = (byte) DIRECTION_IN;
		byte[] length = FpCoder.intTo4Byte(4096);
		System.arraycopy(length, 0, preBytes, 8, 4);
		setReadReady();

		long start = System.currentTimeMillis();
		while (true) {
			// 取消
			if (nCancelFlag == 1) {
				nCancelFlag = 0;
				nRet = FpConst.TCY_CNCL;
				cmdIdle();
				break;
			}
			// 交易超时
			if (isFuncTimeout() == true) {
				nRet = FpConst.TCY_TMOT;
				Logs.i(TAG, "交易超时...");
				cmdIdle();
				break;
			}

			// 发送usb前缀指令
			nRet = write(preBytes);
			Logs.i(TAG, "revCmd ret:" + nRet);
			HexOut(TAG, preBytes, 0, preBytes.length);

			// 接收数据
			nRet = read(mRecvTempBuf, 0, mRecvTempBuf.length);

			// 接收usb后缀数据
			read(bSendBuf, 0, 13);

			if (nRet < 0) {
				return FpConst.TCY_NLNK;
			} else if (nRet == 4096) {
				complete();
			}

			// 校验指令
			for (int i = 0; i < nRet; i++) {
				saveByte(mRecvTempBuf[i]);

				if ((readcomm & 0x80) == 0) {
					continue;
				}
				if (bRecvBuf[0] != mCmdID) {
					setReadReady();
					continue;
				}
				Logs.i(TAG, "图像进度：" + bRecvBuf[5]);

				/* 处理图像传输数据包 */
				if (bRecvBuf[5] == FpConst.RET_IMAGEPARAM) {// 处理图像参数数据包
					width = (bRecvBuf[9] & 0xff) * 256 + (bRecvBuf[10] & 0xff);
					height = (bRecvBuf[11] & 0xff) * 256
							+ (bRecvBuf[12] & 0xff);
					Logs.i(TAG, "width:" + width + " height:" + height);
					mWidth = width - width % 4;
					mHeight = height - height % 4;
					imageBuffer = new byte[mWidth * mHeight];
					isImageOk = true;
					setReadReady();
					continue;
				} else if (bRecvBuf[5] == FpConst.RET_IMAGELINE) {
					if (isImageOk) {
						row = (bRecvBuf[9] & 0xff) * 256
								+ (bRecvBuf[10] & 0xff);
						if (row < height) {
							System.arraycopy(bRecvBuf, 13, imageBuffer, row
									* mWidth, mWidth);
						}
						if (row == height - 1) {
							// 收到最后一行，图像上传 processImage
							mImageBuffer = imageBuffer;
						}
						setReadReady();
						continue;
					}
				}
				if (bRecvBuf[5] != FpConst.RET_SETVERIFY) {
					setReadReady();
					break;
				}

				Logs.e(TAG, "Error:" + bRecvBuf[6]);
				switch (bRecvBuf[6]) {
				case FpConst.MSG_CAPTUREIMAGE:
					sendMsg(FpConst.TCY_CAPTUREIMAGE, null);
					break;
				case FpConst.MSG_VERIFY_START:
					Logs.i(TAG, "认证开始...");
					sendMsg(FpConst.TCY_VERIFY_START, null);
					break;
				case FpConst.MSG_SUCCESS:
					fid = bRecvBuf[9]; // 获得比对指纹数据的指纹标识值
					fid = (fid << 8) + bRecvBuf[10];
					fid = (fid << 8) + bRecvBuf[11];
					fid = (fid << 8) + bRecvBuf[12];
					// nRet = (int)
					// (bRecvBuf[13]|((fid>>8)<<8));//获得比对分数和指纹id.ret=(指纹id<<8)|分数
					nRet = FpConst.TCY_SUCC;
					isImageOk = false;// 本次图像处理完毕，准备接收下幅图像
					isVerifyDone = true;// 本次比对已结束
					mVerifyScore = bRecvBuf[13] & 0xff;
					Logs.i(TAG, "比对分数：" + mVerifyScore);
					break;
				case FpConst.MSG_FAILURE:
					sendMsg(FpConst.TCY_FAIL, null);
					isImageOk = false;// 本次图像处理完毕，准备接收下幅图像
					isVerifyDone = true; // 本次比对已结束
					nRet = FpConst.TCY_FAIL;
					break;
				case FpConst.MSG_MEMORY_ERROR:
					sendMsg(FpConst.TCY_FLSH, null);
					isImageOk = false;// 本次图像处理完毕，准备接收下幅图像
					isVerifyDone = true;// 本次比对已结束
					nRet = FpConst.TCY_FLSH;
					break;
				case FpConst.MSG_ERROR:
					sendMsg(FpConst.TCY_FAIL, null);
					isImageOk = false;// 本次图像处理完毕，准备接收下幅图像
					isVerifyDone = true;// 本次比对已结束
					nRet = FpConst.TCY_FAIL;
					break;
				case FpConst.MSG_NOTEXIST:
					sendMsg(FpConst.TCY_NOT_EXISTS, null);
					nRet = FpConst.TCY_NOT_EXISTS;
					isImageOk = false;// 本次图像处理完毕，准备接收下幅图像
					isVerifyDone = true;// 本次比对已结束
					break;
				case FpConst.MSG_BADIMAGE:
					sendMsg(FpConst.TCY_NOFP, null);
					nRet = FpConst.TCY_NOFP;
					isImageOk = false;// 本次图像处理完毕，准备接收下幅图像
					isVerifyDone = true;// 本次比对已结束
					break;
				default:
					sendMsg(FpConst.TCY_FAIL, null);
					nRet = FpConst.TCY_FAIL;
					isImageOk = false;
					isVerifyDone = true;
					break;
				}

				setReadReady();
				break;
				/* 模块内会重复下一次比对，因此发CMD_NONE要求模块停止比对，进入空转状态。 */

			}
			if (isVerifyDone) {// 如果比对结束，不再循环。退出
				Log.i(TAG, "退出验证...");
				cmdIdle();
				break;
			}
		}

		return nRet;
	}

	/**
	 * 标记读取完成
	 */
	private void complete() {
		int nRet;
		// 发送16字节全0包
		byte[] head = { 0x55, 0x53, 0x42, 0x43,//
				(byte) 0xd0, (byte) 0xd9, (byte) 0x85, (byte) 0xf2,//
				0x10, 0x00, 0x00, 0x00,//
				0x00, 0x00, 0x10, (byte) 0xef,
				0x00,0x00,0x00,0x00,
				0x00,0x00,0x00,0x00,
				0x00,0x00,0x00,0x00,
				0x00,0x00,0x00};//
		nRet = write(head);
		
		// 发送内容
		byte[] content = new byte[16];
		nRet = write(content);
		
		// 接收usb后缀数据
		nRet = read(bSendBuf, 0, 13);
	}

	/**
	 * 将指纹数据下载至模块
	 * 
	 * @param fid
	 * @param data
	 * @return
	 */
	private int setTemplate2ndID(long fid, byte[] data) {
		int nRet = FpConst.TCY_FAIL;
		byte[] buffer;
		mCurrentRetCode = FpConst.RET_SETTEMPLATE;
		if (data == null || data.length != 512) {
			Logs.i(TAG, "template length:" + data.length);
			return FpConst.TCY_PARA;
		}
		byte[] convertData = FpConst.ConvertID2(data, 0);
		if (convertData == null) {
			Logs.i(TAG, "convertData=" + null);
			return FpConst.TCY_PARA;
		}
		int size = convertData.length;
		buffer = new byte[4 + 2 + size];
		fid = (fid << 8);
		buffer[0] = (byte) ((fid >>> 24) & 0xFF);
		buffer[1] = (byte) ((fid >>> 16) & 0xFF);
		buffer[2] = (byte) ((fid >>> 8) & 0xFF);
		buffer[3] = (byte) (fid & 0xFF);
		buffer[4] = (byte) ((size >>> 8) & 0xFF);
		buffer[5] = (byte) (size & 0xFF);
		System.arraycopy(convertData, 0, buffer, 6, size);
		convertData = null;

		isCommTimeout(nTimeoutOfFunc);

		sendCmd((byte) FpConst.CMD_SETTEMPLATE, buffer, 6 + size, 0);

		nRet = revCmd();

		return nRet;
	}

	/**
	 * 删除所有指纹
	 * 
	 * @return
	 */
	private int clear() {
		int nRet = FpConst.TCY_FAIL;
		byte[] data = new byte[4];
		mCurrentRetCode = FpConst.RET_SETDELETE;

		isCommTimeout(nTimeoutOfComm);

		sendCmd((byte) FpConst.CMD_SETDELETE, data, 4, 0);

		nRet = revCmd();

		return nRet;
	}

	/**
	 * 发送空指令停止当前任务
	 * 
	 * @return
	 */
	public FingerResult idle() {
		FingerResult result = new FingerResult();
		cmdIdle();
		return result;
	}
	
	/**
	 * 获取设备sn号
	 */
	public FingerResult getSn() {
		Logs.i(TAG, "getSn...");
		isFuncTimeout(nTimeoutOfComm);
		FingerResult result = new FingerResult();
		int ret = getDeviceSn();
		result.setStatus(ret);
		if (ret == FpConst.TCY_SUCC) {
			result.setResult(mSn);
		}

		return result;
	}

	/**
	 * 设置设备波特率
	 * 
	 * @param baudRate
	 * @return
	 */
	public FingerResult setBaud(int baudRate) {
		Logs.i(TAG, "setBaud...");
		isFuncTimeout(nTimeoutOfComm);
		FingerResult result = new FingerResult();
		result.setStatus(setDeviceBaud());

		return result;
	}

	/**
	 * 设置设备安全等级
	 * 
	 * @param level
	 * @return
	 */
	public FingerResult setLevel(int level) {
		Logs.i(TAG, "setLevel...");
		isFuncTimeout(nTimeoutOfComm);
		FingerResult result = new FingerResult();
		result.setStatus(setDeviceLevel());

		return result;
	}

	/**
	 * 获取设备波特率
	 * 
	 * @return
	 */
	public FingerResult getBaud() {
		Logs.i(TAG, "getBaud...");
		isFuncTimeout(nTimeoutOfComm);
		FingerResult result = new FingerResult();
		int ret = getDeviceBaud();
		result.setStatus(ret);
		if (ret == FpConst.TCY_SUCC) {
			result.setResult(mBaudState + "");
		}

		return result;
	}

	/**
	 * 获取设备安全级别
	 * 
	 * @return
	 */
	public FingerResult getLevel() {
		Logs.i(TAG, "getLevel...");
		isFuncTimeout(nTimeoutOfComm);
		FingerResult result = new FingerResult();
		int ret = getDeviceLevel();
		result.setStatus(ret);
		if (ret == FpConst.TCY_SUCC) {
			result.setResult(mSecurityLevel + "");
		}

		return result;
	}

	/**
	 * 设置指纹注册所需的图像个数
	 * 
	 * @param newCount
	 * @return
	 */
	public FingerResult setEnrollImageCount(int newCount) {
		Logs.i(TAG, "setEnrollImageCount...");
		isFuncTimeout(nTimeoutOfComm);
		FingerResult result = new FingerResult();
		result.setStatus(setDeviceEnrollImageCount(newCount));

		return result;
	}

	/**
	 * 指纹录入操作
	 * 
	 * @return
	 */
	public FingerResult setEntroll(int timeout, MsgCallback msgCallback) {
		Logs.i(TAG, "setEntroll...");
		mCallback = msgCallback;
		isFuncTimeout(timeout);
		FingerResult result = new FingerResult();
		int ret = setDeviceEntroll(FpConfig.isEnrollDownload());
		result.setStatus(ret);
		if (ret == FpConst.TCY_SUCC) {
			result.setTemplate(mFpTemplate);
			// if (mImageBuffer != null) {
			// FpImage fpImage = new FpImage();
			// fpImage.saveRawAsBmpBuf(mImageBuffer, mWidth, mHeight);
			// result.setImgBytes(fpImage.getBmpImgBuf());
			// mImageBuffer = null;
			// }
		}
		mCallback = null;

		return result;
	}

	/**
	 * 指纹验证操作
	 * 
	 * @return
	 */
	public FingerResult setVerify(long fid, int timeout, MsgCallback callback) {
		Logs.i(TAG, "setVerify...");
		mCallback = callback;
		isFuncTimeout(timeout);
		FingerResult result = new FingerResult();
		int ret = setDeviceVerify(fid, FpConfig.isVerifyDownload());
		result.setStatus(ret);
		if (ret == FpConst.TCY_SUCC) {
			result.setScore(100 * mVerifyScore / 255);
			if (mImageBuffer != null) {
				FpImage fpImage = new FpImage();
				fpImage.saveRawAsBmpBuf(mImageBuffer, mWidth, mHeight);
				result.setImgBytes(fpImage.getBmpImgBuf());
				mImageBuffer = null;
			}
		}
		mVerifyScore = 0;
		mCallback = null;

		return result;
	}

	/**
	 * 设置指纹数据
	 * 
	 * @return
	 */
	public FingerResult setTemplate(long id, byte[] data) {
		Logs.i(TAG, "setTemplate...");
		isFuncTimeout(nTimeoutOfFunc);
		FingerResult result = new FingerResult();
		result.setStatus(setTemplate2ndID(id, data));

		return result;
	}

	private void sleep(long millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
